import 'package:flutter/material.dart';

/// 形状裁剪对象
class ClipDecoration extends Decoration {
  const ClipDecoration({
    this.color,
    this.clipper,
    this.borderColor,
    this.borderWidth,
    this.boxShadow,
    this.gradient,
  });

  final Color? color;
  final CustomClipper<Path>? clipper;
  final List<BoxShadow>? boxShadow;
  final Gradient? gradient;

  final Color? borderColor;
  final double? borderWidth;

  @override
  BoxPainter createBoxPainter([VoidCallback? onChanged]) {
    return _BoxDecorationPainter(this, onChanged);
  }
}

class _BoxDecorationPainter extends BoxPainter {
  _BoxDecorationPainter(this._decoration, VoidCallback? onChanged) : super(onChanged);

  final ClipDecoration _decoration;

  Paint? _cachedBackgroundPaint;

  Paint _getBackgroundPaint(Rect rect, TextDirection? textDirection) {
    if (_cachedBackgroundPaint == null) {
      final Paint paint = Paint();
      if (_decoration.color != null) paint.color = _decoration.color!;
      _cachedBackgroundPaint = paint;
    }

    return _cachedBackgroundPaint!;
  }

  void _paintBox(Canvas canvas, Rect rect, Paint paint, TextDirection? textDirection) {
    if (_decoration.clipper == null) {
      canvas.drawRect(rect, paint);
    } else {
      canvas.save();
      canvas.translate(rect.topLeft.dx, rect.topLeft.dy);
      canvas.drawPath(_decoration.clipper!.getClip(rect.size), paint);
      canvas.restore();
    }
  }

  void _paintShadows(Canvas canvas, Rect rect, TextDirection? textDirection) {
    if (_decoration.boxShadow == null) return;
    for (final BoxShadow boxShadow in _decoration.boxShadow!) {
      final Paint paint = boxShadow.toPaint();
      final Rect bounds = rect.shift(boxShadow.offset).inflate(boxShadow.spreadRadius);
      _paintBox(canvas, bounds, paint, textDirection);
    }
  }

  void _paintBorder(Canvas canvas, Rect rect) {
    if (_decoration.borderColor != null || _decoration.borderColor != null) {
      final Paint borderPaint = Paint()
        ..style = PaintingStyle.stroke
        ..color = _decoration.borderColor ?? Colors.black
        ..strokeWidth = _decoration.borderWidth ?? 1;

      if (_decoration.clipper != null) {
        canvas.save();
        canvas.translate(rect.topLeft.dx, rect.topLeft.dy);
        canvas.drawPath(_decoration.clipper!.getClip(rect.size), borderPaint);
        canvas.restore();
      } else {
        canvas.drawRect(rect, borderPaint);
      }
    }
  }

  void _paintBackgroundColor(Canvas canvas, Rect rect, TextDirection? textDirection) {
    if (_decoration.color != null || _decoration.gradient != null)
      _paintBox(canvas, rect, _getBackgroundPaint(rect, textDirection), textDirection);
  }

  @override
  void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
    final Rect rect = offset & configuration.size!;
    final TextDirection? textDirection = configuration.textDirection;
    _paintShadows(canvas, rect, textDirection);
    _paintBackgroundColor(canvas, rect, textDirection);
    _paintBorder(canvas, rect);
  }
}
